package com.soyotec.algorithm.algorithm;

import cn.hutool.json.JSONUtil;
import com.soyotec.algorithm.algorithm.process.AlgorithmRunner;
import com.soyotec.algorithm.algorithm.process.ProcFileEditor;
import com.soyotec.algorithm.context.OptAlgorithmContext;
import com.soyotec.algorithm.context.OptAlgorithmLogContext;
import com.soyotec.algorithm.enums.ExecutionStatusEnum;
import com.soyotec.algorithm.enums.LanguageType;
import com.soyotec.algorithm.problem.Problem;
import com.soyotec.algorithm.solution.ValueWrapSolution;
import com.soyotec.algorithm.solution.value.SolutionValue;
import com.soyotec.algorithm.status.ExecutionStatus;
import com.soyotec.algorithm.util.OPTFileUtil;
import com.soyotec.algorithm.util.PropertiesLoader;
import com.soyotec.algorithm.util.SolutionCache;
import com.soyotec.algorithm.util.file.FileResolver;
import com.soyotec.algorithm.value.DoubleValue;
import com.soyotec.algorithm.value.IntegerValue;
import com.soyotec.algorithm.value.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.converters.ArffLoader;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public abstract class AbstractOPTProcessAlgorithmPlugin implements OPTProcessAlgorithmPlugin {
    private String[] commandLines;
    private Problem<ValueWrapSolution> problem;
    private FileResolver fileResolver = new FileResolver();
    private ProcFileEditor fileEditor = new ProcFileEditor();
    private OptAlgorithmContext optAlgorithmContext;
    private Map<String, Object> algorithmParameter;
    private ExecutorService executorService = Executors.newCachedThreadPool();
    //    private Map<Integer, ValueWrapSolution> solutionMap;
    private SolutionCache solutionCache;
    private List<Double> resultVariables;
    private PropertiesLoader propertiesLoader;

    private File mainFile;

    private  static Logger logger=  LoggerFactory.getLogger(AbstractOPTProcessAlgorithmPlugin.class);

    @Override
    public void run(){
        AlgorithmRunner runner=new AlgorithmRunner(optAlgorithmContext);
        exec(runner);//把现在SQPDFO run()方法里除了生成命令行之外的代码贴过来
    }

    protected void exec(AlgorithmRunner runner) {
        try {
            init();
            if (commandLines == null || commandLines.length== 0) {
                return;
            }
            //执行目录
            String mode = propertiesLoader.getMode();
            File dir = LanguageType.findByTitle(mode).getExecDirFile(optAlgorithmContext,mainFile.getName());
            System.out.println("当前执行目录：" + dir.getAbsolutePath());
            StringBuilder  params=new StringBuilder();
//            int numInstances = 0;
//            int numAttribute = 0;
            int result= runner.runAlgorithm(commandLines,dir,(writer, reader, errorReader) -> {
//                System.out.println("9------------处理子流程循环---------------");
                String  line;
                boolean ready=false;
                try {
                    while ((line = reader.readLine()) != null) {
//                        System.out.println("9------------命令接收---------------："+line);
//                        System.out.println("0000000000000000000000000000000000iflog:"+line.startsWith("!"));
                        if(line.startsWith("#")){//命令
                            ExecutionStatus executionStatus= JSONUtil.toBean(line.substring(1),ExecutionStatus.class);
                            if(ExecutionStatusEnum.WRITE_DESIGN_VARIABLES.getCode().equals(executionStatus.getCode())){
                                //开始接收数据
                                ready=true;
//                                System.out.println("9------------开始接收参数数据---------------");
                            }else if(ExecutionStatusEnum.ALGORITHM_RUN_COMPUTED.getCode().equals(executionStatus.getCode())){ //算法执行完毕
//                                reader.close();啥都不用干啊
//                                errorReader.close();
//                                writer.close();
//                                System.out.println("9------------算法执行完毕---------------");
                            }else if(ExecutionStatusEnum.WRITE_DESIGN_VARIABLES_END.getCode().equals(executionStatus.getCode())){ //参数接受完毕
//                                System.out.println("9------------接收参数完毕---------------");
                                if(ready){
                                    ready = false;
                                    if(!"".equals(params.toString())){
//                                        System.out.println("输入参数arrf:" + params.toString());
                                        List<ValueWrapSolution> doubleSolutionList = createSoulutionList(params);
//                                System.out.println("---------------------------------doubleSolutionList："+doubleSolutionList.size());
                                        problem.evaluate(doubleSolutionList); //执行计算
                                        //回写数据 到算法
                                        params.delete(0, params.length());//清空内容池子
                                        //---------------该发送结果了
                                        for (ValueWrapSolution doubleSolution : doubleSolutionList) {
                                            List<SolutionValue> variables = doubleSolution.getVariables();
                                            List<Double> value = new ArrayList<>();
                                            for(SolutionValue solutionValue : variables){
                                                value.add(solutionValue.getValue().getDoubleValue());
                                            }
                                            solutionCache.put(arrayHashCode(value), doubleSolution);
                                        }
                                        //4 回写约束和目标
                                        sentSubflowEnd(writer);
                                        sendObjectBegin(writer);
                                        String content=fileEditor.updateRuntimeObjectiveFile(doubleSolutionList);
//                                        System.out.println("目标结果arff：" + content);
//                                System.out.println("================send object content:"+content);
                                        writer.write(content);//写object内容
                                        sendObjectEnd(writer);
                                        content=fileEditor.updateRuntimeConstraintFile(doubleSolutionList);
                                        if(!content.trim().equals("")) {
                                            sendConstraintsBegin(writer);
//                                    System.out.println("================send constriainted content:" + content);
//                                            System.out.println("约束结果：" + content);
                                            writer.write(content);//写constraint内容
                                            sendConstraintsEnd(writer);

                                        }
                                        //5 回写权重化目标
                                        sendWeightObjectBegin(writer);
                                        content=fileEditor.updateRuntimeWeightObjectiveFile(doubleSolutionList);
//                                        System.out.println("权重化目标结果arff：" + content);
//                                        System.out.println("================send weightObjective content:" + content);
                                        writer.write(content);
                                        sendWeightObjectEnd(writer);

                                        sendResultEnd(writer);
                                        writer.newLine();
                                        writer.flush();
                                    } else {
                                        sendResultEnd(writer);
                                        writer.newLine();
                                        writer.flush();
                                    }
                                } else {
                                    sendResultEnd(writer);
                                    writer.newLine();
                                    writer.flush();
                                }

                                // 发送结果完毕
//                                System.out.println("9------------发送结果完成---------------");
                            }
                        }else if(line.startsWith("!")){
//                            System.out.println("------------------log:"+line);
                            runner.writeInfoLog("log:"+line);
                            logger.info("log:"+line);
                        } else{//数据
                            if(ready) {
//                                System.out.println("------------------params data:"+line);
                                params.append(line).append("\n");//变量数据加入内容池子
                            }
                        }

                    }
                }catch (IOException e){
                    e.printStackTrace();
                    return;
                }
            });
//            System.out.println("==================算法执行完毕=============result："+result);
            if(result!=-1){///这个部分也是不需要修改的 oh yeah =-1是执行出错了
                //读取result.arff   这里不需要在读了
//                Instances resultInstances = fileResolver.resultFileResolver(optAlgorithmContext.getWorkDir().getAbsolutePath());
//                int  numInstances = resultInstances.numInstances();
//                resultVariables = new ArrayList<>();
//                for (int i = 0; i < numInstances; i++) {
//                    ValueWrapSolution doubleSolution = problem.createSolution();
//                    int variables = doubleSolution.getNumberOfVariables();
//                    Instance instance = resultInstances.get(i);
//                    for (int j = 0; j < variables; j++) {
//                        resultVariables.add(Double.valueOf(instance.toString(j + 1, 20)));
//                    }
//                }
            }
            logger.info("--------------------process algorithm plugin end-------------------------");
//            System.out.println("--------------------process algorithm plugin end-------------------------");
        } catch (Exception e) {
            logger.error("-------------------------------process algorithm plugin------------------------------exception:",e);
//            System.out.println("-------------------------------process algorithm plugin------------------------------exception:"+e);
//            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }finally {
            runner.destory();

        }
    }

    private void sendResultEnd(BufferedWriter writer) throws IOException {
//        System.out.println("===============================sendResultEnd");
        ExecutionStatus status;
        status=new ExecutionStatus();
        status.setCode(ExecutionStatusEnum.SEND_RESULT_END.getCode());
//        status.setMessage(ExecutionStatusEnum.SEND_RESULT_END.getTitle());
        writer.write(status.toCmd());
        writer.newLine();
    }

    private void sendConstraintsEnd(BufferedWriter writer) throws IOException {
//        System.out.println("===============================sendConstraintsEnd");
        ExecutionStatus status;
        status=new ExecutionStatus();
        status.setCode(ExecutionStatusEnum.SEND_CONSTRAINTS_END.getCode());
//        status.setMessage(ExecutionStatusEnum.SEND_CONSTRAINTS_END.getTitle());
        writer.write(status.toCmd());
        writer.newLine();
    }

    private void sendConstraintsBegin(BufferedWriter writer) throws IOException {
        ExecutionStatus status;
        status=new ExecutionStatus();
        status.setCode(ExecutionStatusEnum.SEND_CONSTRAINTS_BEGIN.getCode());
//        status.setMessage(ExecutionStatusEnum.SEND_CONSTRAINTS_BEGIN.getTitle());
        writer.write(status.toCmd());
        writer.newLine();
    }

    private void sendObjectEnd(BufferedWriter writer) throws IOException {
        ExecutionStatus status;
        status=new ExecutionStatus();
        status.setCode(ExecutionStatusEnum.SEND_OBJECTIVES_END.getCode());
//        status.setMessage(ExecutionStatusEnum.SEND_OBJECTIVES_END.getTitle());
        writer.write(status.toCmd());
        writer.newLine();
    }

    private void sendWeightObjectEnd(BufferedWriter writer) throws IOException {
        ExecutionStatus status;
        status=new ExecutionStatus();
        status.setCode(ExecutionStatusEnum.SEND_WEIGHT_OBJECTIVES_END.getCode());
//        status.setMessage(ExecutionStatusEnum.SEND_OBJECTIVES_END.getTitle());
        writer.write(status.toCmd());
        writer.newLine();
    }

    private void sendObjectBegin(BufferedWriter writer) throws IOException {
        ExecutionStatus status;
        status=new ExecutionStatus();
        status.setCode(ExecutionStatusEnum.SEND_OBJECTIVES_BEGIN.getCode());
//        status.setMessage(ExecutionStatusEnum.SEND_OBJECTIVES_BEGIN.getTitle());
        writer.write(status.toCmd());
        writer.newLine();
    }

    private void sendWeightObjectBegin(BufferedWriter writer) throws IOException {
        ExecutionStatus status;
        status=new ExecutionStatus();
        status.setCode(ExecutionStatusEnum.SEND_WEIGHT_OBJECTIVES_BEGIN.getCode());
//        status.setMessage(ExecutionStatusEnum.SEND_OBJECTIVES_BEGIN.getTitle());
        writer.write(status.toCmd());
        writer.newLine();
    }

    private void sentSubflowEnd(BufferedWriter writer) throws IOException {
        ExecutionStatus  status=new ExecutionStatus();
        status.setCode(ExecutionStatusEnum.SUB_PROCESS_COMPUTED.getCode());
//        status.setMessage(ExecutionStatusEnum.SUB_PROCESS_COMPUTED.getTitle());
        writer.write(status.toCmd());
        writer.newLine();
    }

    private List<ValueWrapSolution> createSoulutionList(StringBuilder params) throws IOException {

        ArffLoader loader = new ArffLoader();
        InputStream inputStream=new ByteArrayInputStream(params.toString().getBytes());
        loader.setSource(inputStream);
        Instances varInstances = loader.getDataSet();
        int  numInstances = varInstances.numInstances();
        int numAttribute = varInstances.numAttributes();
        List<ValueWrapSolution> doubleSolutionList = new ArrayList<>();
        for (int i = 0; i < numInstances; i++) {
            ValueWrapSolution doubleSolution = problem.createInitSolution();
            for (int j = 1; j < numAttribute; j++) {
                Instance instance = varInstances.get(i);
                Value value = doubleSolution.getVariableValue(j-1).getValue();
                String instanceValue = instance.toString(j, 20);
                // 这里用?判断也没办法，instance源码里判断了isNAN，但是他会返回? 所以用?判断
                if("?".equals(instanceValue)){
                    if(optAlgorithmContext!=null&&optAlgorithmContext instanceof OptAlgorithmLogContext) {
                        OptAlgorithmLogContext logContext = (OptAlgorithmLogContext) optAlgorithmContext;
                        logContext.writeErrorLog("算法传入 funcEval 的参数不合法：NaN。请修改算法。",
                                "The parameter passed into funcEval by the algorithm is illegal: NaN. Please modify the algorithm.");
                    }
                    throw new RuntimeException("算法传入 funcEval 的参数不合法：NaN。请修改算法。");
                }
                if(value instanceof IntegerValue){
                    IntegerValue integerValue = (IntegerValue) value;
                    integerValue.setDoubleValue(Double.valueOf(instance.toString(j, 20)));
                } else if(value instanceof DoubleValue){
                    DoubleValue doubleValue = (DoubleValue) value;
                    doubleValue.setDoubleValue(Double.valueOf(instance.toString(j, 20)));
                }

            }
            doubleSolutionList.add(doubleSolution);
        }
        return doubleSolutionList;
    }

    public void init() {
        //创建各种文件
        if (problem != null) {
            ValueWrapSolution solution = problem.createInitSolution();
            fileEditor.createVariableFile(optAlgorithmContext.getWorkDir().getAbsolutePath(), solution);
            fileEditor.createVariableDiscreteFile(optAlgorithmContext.getWorkDir().getAbsolutePath(), solution);
            fileEditor.createConstraintFile(optAlgorithmContext.getWorkDir().getAbsolutePath(), solution);
            fileEditor.createObjectiveFile(optAlgorithmContext.getWorkDir().getAbsolutePath(), solution);
            fileEditor.createObjectiveActualFile(optAlgorithmContext.getWorkDir().getAbsolutePath(), solution);
            fileEditor.createAlgorithmParameterFile(optAlgorithmContext.getWorkDir().getAbsolutePath(), algorithmParameter,getName());
            fileEditor.createRuntimeVariableFile(optAlgorithmContext.getWorkDir().getAbsolutePath(), solution);
            fileEditor.createRuntimeConstraintFile(optAlgorithmContext.getWorkDir().getAbsolutePath(), solution);
            fileEditor.createRuntimeObjectiveFile(optAlgorithmContext.getWorkDir().getAbsolutePath(), solution);
            fileEditor.createResultFile(optAlgorithmContext.getWorkDir().getAbsolutePath(), solution);
        }
    }



    public int arrayHashCode(double[] array) {
        if (array == null) {
            return 0;
        }
        StringBuffer codeBuffer = new StringBuffer();
        for (double v : array) {
            codeBuffer.append(v);
        }
        return codeBuffer.toString().hashCode();
    }

    public int arrayHashCode(List<Double> array) {
        if (array == null) {
            return 0;
        }
        StringBuffer codeBuffer = new StringBuffer();
        for (double v : array) {
            codeBuffer.append(v);
        }
        return codeBuffer.toString().hashCode();
    }

    public ValueWrapSolution getResult() {
        ValueWrapSolution valueWrapSolution=null;
        if (resultVariables != null) {

            valueWrapSolution =(ValueWrapSolution) solutionCache.get(arrayHashCode(resultVariables));
        }
        solutionCache.destory();
        return valueWrapSolution;
    }

    public static void main(String[] args){
        File file = new File("C:\\Users\\Administrator\\Desktop\\dakota.txt");
        System.out.println(file.getAbsolutePath());
    }

    @Override
    public void initCommandLines(String[] commandLines){
        this.commandLines = commandLines;
    }

    public String[] createCommandLines(AlgorithmRunner algorithmRunner){
        String fileName = propertiesLoader.getMainFile();
        logger.info("mainFileName:" + fileName);
        mainFile = OPTFileUtil.findMainFile(this.getOptAlgorithmContext().loadOtherFiles(),fileName);
        if(mainFile != null){
            logger.info("mainFilePath:" + mainFile.getAbsolutePath());
            String mode = propertiesLoader.getMode();
            LanguageType.findByTitle(mode).copyFileToExecDir(optAlgorithmContext);
            String[] commandLines = LanguageType.findByTitle(mode).getCommandLines(mainFile,algorithmRunner,optAlgorithmContext);
            logger.info("执行命令:" + Arrays.toString(commandLines));
            return commandLines;
        } else {
            throw new RuntimeException("未找到执行主文件");
        }
    }

    public AlgorithmRunner createAlgorithmRunner(){
        String mode = propertiesLoader.getMode();
        AlgorithmRunner algorithmRunner = LanguageType.findByTitle(mode).getAlgorithmRunner(optAlgorithmContext);
        return algorithmRunner;
    }

    public void createPropertiesLoader(){
        propertiesLoader = new PropertiesLoader(optAlgorithmContext);
    }


    public OptAlgorithmContext getOptAlgorithmContext() {
        return optAlgorithmContext;
    }

    public void setOptAlgorithmContext(OptAlgorithmContext optAlgorithmContext) {
        this.optAlgorithmContext = optAlgorithmContext;
        this.solutionCache = new SolutionCache(optAlgorithmContext);
    }

    public Problem<ValueWrapSolution> getProblem() {
        return problem;
    }

    public void setProblem(Problem<ValueWrapSolution> problem) {
        this.problem = problem;
    }

    public Map<String, Object> getAlgorithmParameter() {
        return algorithmParameter;
    }

    public void setAlgorithmParameter(Map<String, Object> algorithmParameter) {
        this.algorithmParameter = algorithmParameter;
    }

//    public Map<Integer, ValueWrapSolution> getSolutionMap() {
//        return solutionMap;
//    }
//
//    public void setSolutionMap(Map<Integer, ValueWrapSolution> solutionMap) {
//        this.solutionMap = solutionMap;
//    }

    @Override
    public String getName() {
        if(this.getOptAlgorithmContext() != null){
            PropertiesLoader propertiesLoader = new PropertiesLoader(this.getOptAlgorithmContext());
            String algorithmName = propertiesLoader.getAlgorithmName();
            if(algorithmName != null){
                return algorithmName;
            }
        }
        return "";
    }
}
